package com.xiaoleilu.hutool.io;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PushbackInputStream;
import java.io.PushbackReader;
import java.io.Reader;
import java.io.Serializable;
import java.io.Writer;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.charset.Charset;
import java.util.Collection;

import org.dromara.hutool.core.lang.Assert;
import org.dromara.hutool.core.util.CharsetUtil;

import com.xiaoleilu.hutool.exceptions.UtilException;

/**
 * IO工具类<br>
 * IO工具类只是辅助流的读写，并不负责关闭流。原因是流可能被多次读写，读写关闭后容易造成问题。
 *
 * @author xiaoleilu
 */
public class IoUtil {

    /**
     * 默认缓存大小
     */
    public static final int DEFAULT_BUFFER_SIZE = 1024;

    /**
     * 默认缓存大小
     */
    public static final int DEFAULT_LARGE_BUFFER_SIZE = 4096;

    /**
     * 数据流末尾
     */
    public static final int EOF = -1;

    // -------------------------------------------------------------------------------------- Copy start
    /**
     * 将Reader中的内容复制到Writer中 使用默认缓存大小
     *
     * @param reader Reader
     * @param writer Writer
     * @return 拷贝的字节数
     * @throws IORuntimeException IO异常
     */
    public static long copy(Reader reader, Writer writer) throws IORuntimeException {
        return org.dromara.hutool.core.io.IoUtil.copy(reader, writer);
    }

    /**
     * 将Reader中的内容复制到Writer中
     *
     * @param reader Reader
     * @param writer Writer
     * @param bufferSize 缓存大小
     * @return 传输的byte数
     * @throws IORuntimeException IO异常
     */
    public static long copy(Reader reader, Writer writer, int bufferSize) throws IORuntimeException {
        return org.dromara.hutool.core.io.IoUtil.copy(reader, writer, bufferSize);
    }

    /**
     * 将Reader中的内容复制到Writer中
     *
     * @param reader Reader
     * @param writer Writer
     * @param bufferSize 缓存大小
     * @param streamProgress 进度处理器
     * @return 传输的byte数
     * @throws IORuntimeException IO异常
     */
    public static long copy(Reader reader, Writer writer, int bufferSize, StreamProgress streamProgress) throws IORuntimeException {
        return org.dromara.hutool.core.io.IoUtil.copy(reader, writer, bufferSize, streamProgress);
    }

    /**
     * 拷贝流，使用默认Buffer大小
     *
     * @param in 输入流
     * @param out 输出流
     * @return 传输的byte数
     * @throws IORuntimeException IO异常
     */
    public static long copy(InputStream in, OutputStream out) throws IORuntimeException {
        return org.dromara.hutool.core.io.IoUtil.copy(in, out);
    }

    /**
     * 拷贝流
     *
     * @param in 输入流
     * @param out 输出流
     * @param bufferSize 缓存大小
     * @return 传输的byte数
     * @throws IORuntimeException IO异常
     */
    public static long copy(InputStream in, OutputStream out, int bufferSize) throws IORuntimeException {
        return org.dromara.hutool.core.io.IoUtil.copy(in, out, bufferSize);
    }

    /**
     * 拷贝流
     *
     * @param in 输入流
     * @param out 输出流
     * @param bufferSize 缓存大小
     * @param streamProgress 进度条
     * @return 传输的byte数
     * @throws IORuntimeException IO异常
     */
    public static long copy(InputStream in, OutputStream out, int bufferSize, StreamProgress streamProgress) throws IORuntimeException {
        return org.dromara.hutool.core.io.IoUtil.copy(in, out, bufferSize, streamProgress);
    }

    /**
     * 拷贝流 thanks to: https://github.com/venusdrogon/feilong-io/blob/master/src/main/java/com/feilong/io/IOWriteUtil.java<br>
     * 本方法不会关闭流
     *
     * @param in 输入流
     * @param out 输出流
     * @param bufferSize 缓存大小
     * @param streamProgress 进度条
     * @return 传输的byte数
     * @throws IORuntimeException IO异常
     */
    public static long copyByNIO(InputStream in, OutputStream out, int bufferSize, StreamProgress streamProgress) throws IORuntimeException {
        return org.dromara.hutool.core.io.IoUtil.copyByNIO(in, out, bufferSize, streamProgress);
    }

    /**
     * 拷贝文件流，使用NIO
     *
     * @param in 输入
     * @param out 输出
     * @return 拷贝的字节数
     * @throws IORuntimeException IO异常
     */
    public static long copy(FileInputStream in, FileOutputStream out) throws IORuntimeException {
        return org.dromara.hutool.core.io.IoUtil.copy(in, out);
    }

    /**
     * 拷贝流，使用NIO，不会关闭流
     *
     * @param in {@link ReadableByteChannel}
     * @param out {@link WritableByteChannel}
     * @param bufferSize 缓冲大小，如果小于等于0，使用默认
     * @param streamProgress {@link StreamProgress}进度处理器
     * @return 拷贝的字节数
     * @throws IORuntimeException IO异常
     */
    public static long copy(ReadableByteChannel in, WritableByteChannel out, int bufferSize, StreamProgress streamProgress) throws IORuntimeException {
        return org.dromara.hutool.core.io.IoUtil.copy(in, out, bufferSize, -1,streamProgress);
    }

    // -------------------------------------------------------------------------------------- Copy end
    // -------------------------------------------------------------------------------------- getReader and getWriter start
    /**
     * 获得一个文件读取器
     *
     * @param in 输入流
     * @param charsetName 字符集名称
     * @return BufferedReader对象
     */
    public static BufferedReader getReader(InputStream in, String charsetName) {
        return org.dromara.hutool.core.io.IoUtil.toReader(in, Charset.forName(charsetName));
    }

    /**
     * 获得一个Reader
     *
     * @param in 输入流
     * @param charset 字符集
     * @return BufferedReader对象
     */
    public static BufferedReader getReader(InputStream in, Charset charset) {
        return org.dromara.hutool.core.io.IoUtil.toReader(in, charset);
    }

    /**
     * 获得{@link BufferedReader}<br>
     * 如果是{@link BufferedReader}强转返回，否则新建。如果提供的Reader为null返回null
     *
     * @param reader 普通Reader，如果为null返回null
     * @return {@link BufferedReader} or null
     * @since 3.0.9
     */
    public static BufferedReader getReader(Reader reader) {
        return org.dromara.hutool.core.io.IoUtil.toBuffered(reader);
    }

    /**
     * 获得{@link PushbackReader}<br>
     * 如果是{@link PushbackReader}强转返回，否则新建
     *
     * @param reader 普通Reader
     * @param pushBackSize 推后的byte数
     * @return {@link PushbackReader}
     * @since 3.1.0
     */
    public static PushbackReader getPushBackReader(Reader reader, int pushBackSize) {
        return org.dromara.hutool.core.io.IoUtil.toPushBackReader(reader, pushBackSize);
    }

    /**
     * 获得一个Writer
     *
     * @param out 输入流
     * @param charsetName 字符集
     * @return OutputStreamWriter对象
     */
    public static OutputStreamWriter getWriter(OutputStream out, String charsetName) {
        return org.dromara.hutool.core.io.IoUtil.toWriter(out, Charset.forName(charsetName));
    }

    /**
     * 获得一个Writer
     *
     * @param out 输入流
     * @param charset 字符集
     * @return OutputStreamWriter对象
     */
    public static OutputStreamWriter getWriter(OutputStream out, Charset charset) {
        return org.dromara.hutool.core.io.IoUtil.toWriter(out, charset);
    }

    // -------------------------------------------------------------------------------------- getReader and getWriter end
    // -------------------------------------------------------------------------------------- read start
    /**
     * 从流中读取内容
     *
     * @param in 输入流
     * @param charsetName 字符集
     * @return 内容
     * @throws IORuntimeException IO异常
     */
    public static String read(InputStream in, String charsetName) throws IORuntimeException {
        return org.dromara.hutool.core.io.IoUtil.read(in, Charset.forName(charsetName));
    }

    /**
     * 从流中读取内容，读取完毕后并不关闭流
     *
     * @param in 输入流，读取完毕后并不关闭流
     * @param charset 字符集
     * @return 内容
     * @throws IORuntimeException IO异常
     */
    public static String read(InputStream in, Charset charset) throws IORuntimeException {
        return org.dromara.hutool.core.io.IoUtil.read(in, charset);
    }

    /**
     * 从流中读取内容，读到输出流中
     *
     * @param in 输入流
     * @return 输出流
     * @throws IORuntimeException IO异常
     */
    public static FastByteArrayOutputStream read(InputStream in) throws IORuntimeException {
    	final FastByteArrayOutputStream out = new FastByteArrayOutputStream();
		copy(in, out);
		return out;
    }

    /**
     * 从Reader中读取String，读取完毕后并不关闭Reader
     *
     * @param reader Reader
     * @return String
     * @throws IORuntimeException IO异常
     */
    public static String read(Reader reader) throws IORuntimeException {
        return org.dromara.hutool.core.io.IoUtil.read(reader);
    }

    /**
     * 从FileChannel中读取UTF-8编码内容
     *
     * @param fileChannel 文件管道
     * @return 内容
     * @throws IORuntimeException IO异常
     */
    public static String readUtf8(FileChannel fileChannel) throws IORuntimeException {
        return org.dromara.hutool.core.io.IoUtil.readUtf8(fileChannel);
    }

    /**
     * 从FileChannel中读取内容，读取完毕后并不关闭Channel
     *
     * @param fileChannel 文件管道
     * @param charsetName 字符集
     * @return 内容
     * @throws IORuntimeException IO异常
     */
    public static String read(FileChannel fileChannel, String charsetName) throws IORuntimeException {
        return org.dromara.hutool.core.io.IoUtil.read(fileChannel, Charset.forName(charsetName));
    }

    /**
     * 从FileChannel中读取内容
     *
     * @param fileChannel 文件管道
     * @param charset 字符集
     * @return 内容
     * @throws IORuntimeException IO异常
     */
    public static String read(FileChannel fileChannel, Charset charset) throws IORuntimeException {
        return org.dromara.hutool.core.io.IoUtil.read(fileChannel, charset);
    }

    /**
     * 从流中读取bytes
     *
     * @param in {@link InputStream}
     * @return bytes
     * @throws IORuntimeException IO异常
     */
    public static byte[] readBytes(InputStream in) throws IORuntimeException {
        return org.dromara.hutool.core.io.IoUtil.readBytes(in);
    }

    /**
     * 读取指定长度的byte数组，不关闭流
     *
     * @param in {@link InputStream}，为null返回null
     * @param length 长度，小于等于0返回空byte数组
     * @return bytes
     * @throws IORuntimeException IO异常
     */
    public static byte[] readBytes(InputStream in, int length) throws IORuntimeException {
        return org.dromara.hutool.core.io.IoUtil.readBytes(in, length);
    }

    /**
     * 读取16进制字符串
     *
     * @param in {@link InputStream}
     * @param length 长度
     * @param toLowerCase true 传换成小写格式 ， false 传换成大写格式
     * @return 16进制字符串
     * @throws IORuntimeException IO异常
     */
    public static String readHex(InputStream in, int length, boolean toLowerCase) throws IORuntimeException {
        return org.dromara.hutool.core.io.IoUtil.readHex(in, length, toLowerCase);
    }

    /**
     * 从流中读取前28个byte并转换为16进制，字母部分使用大写
     *
     * @param in {@link InputStream}
     * @return 16进制字符串
     * @throws IORuntimeException IO异常
     */
    public static String readHex28Upper(InputStream in) throws IORuntimeException {
        return org.dromara.hutool.core.io.IoUtil.readHex(in,28,true);
    }

    /**
     * 从流中读取前28个byte并转换为16进制，字母部分使用小写
     *
     * @param in {@link InputStream}
     * @return 16进制字符串
     * @throws IORuntimeException IO异常
     */
    public static String readHex28Lower(InputStream in) throws IORuntimeException {
        return org.dromara.hutool.core.io.IoUtil.readHex(in,28,false);
    }

    /**
     * 从流中读取内容，读到输出流中
     *
     * @param <T> 读取对象的类型
     * @param in 输入流
     * @return 输出流
     * @throws IORuntimeException IO异常
     * @throws UtilException ClassNotFoundException包装
     */
    public static <T> T readObj(InputStream in) throws IORuntimeException, UtilException {
        return org.dromara.hutool.core.io.IoUtil.readObj(in);
    }

    /**
     * 从流中读取内容，使用UTF-8编码
     *
     * @param <T> 集合类型
     * @param in 输入流
     * @param collection 返回集合
     * @return 内容
     * @throws IORuntimeException IO异常
     */
    public static <T extends Collection<String>> T readUtf8Lines(InputStream in, T collection) throws IORuntimeException {
        return org.dromara.hutool.core.io.IoUtil.readUtf8Lines(in, collection);
    }

    /**
     * 从流中读取内容
     *
     * @param <T> 集合类型
     * @param in 输入流
     * @param charsetName 字符集
     * @param collection 返回集合
     * @return 内容
     * @throws IORuntimeException IO异常
     */
    public static <T extends Collection<String>> T readLines(InputStream in, String charsetName, T collection) throws IORuntimeException {
        return org.dromara.hutool.core.io.IoUtil.readLines(in, Charset.forName(charsetName), collection);
    }

    /**
     * 从流中读取内容
     *
     * @param <T> 集合类型
     * @param in 输入流
     * @param charset 字符集
     * @param collection 返回集合
     * @return 内容
     * @throws IORuntimeException IO异常
     */
    public static <T extends Collection<String>> T readLines(InputStream in, Charset charset, T collection) throws IORuntimeException {
        return org.dromara.hutool.core.io.IoUtil.readLines(in, charset, collection);
    }

    /**
     * 从Reader中读取内容
     *
     * @param <T> 集合类型
     * @param reader {@link Reader}
     * @param collection 返回集合
     * @return 内容
     * @throws IORuntimeException IO异常
     */
    public static <T extends Collection<String>> T readLines(Reader reader, final T collection) throws IORuntimeException {
        return org.dromara.hutool.core.io.IoUtil.readLines(reader, collection);
    }

    /**
     * 按行读取UTF-8编码数据，针对每行的数据做处理
     *
     * @param in {@link InputStream}
     * @param lineHandler 行处理接口，实现handle方法用于编辑一行的数据后入到指定地方
     * @throws IORuntimeException IO异常
     * @since 3.1.1
     */
    public static void readUtf8Lines(InputStream in, LineHandler lineHandler) throws IORuntimeException {
    	readLines(getReader(in, CharsetUtil.UTF_8), lineHandler);
    }

    /**
     * 按行读取数据，针对每行的数据做处理
     *
     * @param in {@link InputStream}
     * @param charset {@link Charset}编码
     * @param lineHandler 行处理接口，实现handle方法用于编辑一行的数据后入到指定地方
     * @throws IORuntimeException IO异常
     * @since 3.0.9
     */
    public static void readLines(InputStream in, Charset charset, LineHandler lineHandler) throws IORuntimeException {
    	readLines(getReader(in, charset), lineHandler);
    }

    /**
     * 按行读取数据，针对每行的数据做处理<br>
     * {@link Reader}自带编码定义，因此读取数据的编码跟随其编码。
     *
     * @param reader {@link Reader}
     * @param lineHandler 行处理接口，实现handle方法用于编辑一行的数据后入到指定地方
     * @throws IORuntimeException IO异常
     */
    public static void readLines(Reader reader, LineHandler lineHandler) throws IORuntimeException {
    	Assert.notNull(reader);
		Assert.notNull(lineHandler);
		
		// 从返回的内容中读取所需内容
		final BufferedReader bReader = getReader(reader);
		String line = null;
		try {
			while ((line = bReader.readLine()) != null) {
				lineHandler.handle(line);
			}
		} catch (IOException e) {
			throw new IORuntimeException(e);
		}

    }

    // -------------------------------------------------------------------------------------- read end
    /**
     * String 转为流
     *
     * @param content 内容
     * @param charsetName 编码
     * @return 字节流
     */
    public static ByteArrayInputStream toStream(String content, String charsetName) {
        return org.dromara.hutool.core.io.IoUtil.toStream(content, Charset.forName(charsetName));
    }

    /**
     * String 转为流
     *
     * @param content 内容
     * @param charset 编码
     * @return 字节流
     */
    public static ByteArrayInputStream toStream(String content, Charset charset) {
        return org.dromara.hutool.core.io.IoUtil.toStream(content, charset);
    }

    /**
     * 文件转为流
     *
     * @param file 文件
     * @return {@link FileInputStream}
     */
    public static FileInputStream toStream(File file) {
        return (FileInputStream) org.dromara.hutool.core.io.IoUtil.toStream(file);
    }

    /**
     * 转换为{@link PushbackInputStream}<br>
     * 如果传入的输入流已经是{@link PushbackInputStream}，强转返回，否则新建一个
     *
     * @param in {@link InputStream}
     * @param pushBackSize 推后的byte数
     * @return {@link PushbackInputStream}
     * @since 3.1.0
     */
    public static PushbackInputStream toPushbackStream(InputStream in, int pushBackSize) {
        return org.dromara.hutool.core.io.IoUtil.toPushbackStream(in, pushBackSize);
    }

    /**
     * 将byte[]写到流中
     *
     * @param out 输出流
     * @param isCloseOut 写入完毕是否关闭输出流
     * @param content 写入的内容
     * @throws IORuntimeException IO异常
     */
    public static void write(OutputStream out, boolean isCloseOut, byte[] content) throws IORuntimeException {
         org.dromara.hutool.core.io.IoUtil.write(out, isCloseOut, content);
    }

    /**
     * 将多部分内容写到流中，自动转换为UTF-8字符串
     *
     * @param out 输出流
     * @param isCloseOut 写入完毕是否关闭输出流
     * @param contents 写入的内容，调用toString()方法，不包括不会自动换行
     * @throws IORuntimeException IO异常
     * @since 3.1.1
     */
    public static void writeUtf8(OutputStream out, boolean isCloseOut, Object... contents) throws IORuntimeException {
         org.dromara.hutool.core.io.IoUtil.writeUtf8(out, isCloseOut, (CharSequence[]) contents);
    }

    /**
     * 将多部分内容写到流中，自动转换为字符串
     *
     * @param out 输出流
     * @param charsetName 写出的内容的字符集
     * @param isCloseOut 写入完毕是否关闭输出流
     * @param contents 写入的内容，调用toString()方法，不包括不会自动换行
     * @throws IORuntimeException IO异常
     */
    public static void write(OutputStream out, String charsetName, boolean isCloseOut, Object... contents) throws IORuntimeException {
         org.dromara.hutool.core.io.IoUtil.writeStrs(out, Charset.forName(charsetName), isCloseOut, (CharSequence[]) contents);
    }

    /**
     * 将多部分内容写到流中，自动转换为字符串
     *
     * @param out 输出流
     * @param charset 写出的内容的字符集
     * @param isCloseOut 写入完毕是否关闭输出流
     * @param contents 写入的内容，调用toString()方法，不包括不会自动换行
     * @throws IORuntimeException IO异常
     * @since 3.0.9
     */
    public static void write(OutputStream out, Charset charset, boolean isCloseOut, Object... contents) throws IORuntimeException {
         org.dromara.hutool.core.io.IoUtil.writeStrs(out, charset, isCloseOut, (CharSequence[]) contents);
    }

    /**
     * 将多部分内容写到流中
     *
     * @param out 输出流
     * @param isCloseOut 写入完毕是否关闭输出流
     * @param contents 写入的内容
     * @throws IORuntimeException IO异常
     */
    public static void writeObjects(OutputStream out, boolean isCloseOut, Serializable... contents) throws IORuntimeException {
         org.dromara.hutool.core.io.IoUtil.writeObjects(out, isCloseOut, contents);
    }

    /**
     * 关闭<br>
     * 关闭失败不会抛出异常
     *
     * @param closeable 被关闭的对象
     */
    public static void close(Closeable closeable) {
         org.dromara.hutool.core.io.IoUtil.closeQuietly(closeable);
    }

    /**
     * 关闭<br>
     * 关闭失败不会抛出异常
     *
     * @param closeable 被关闭的对象
     */
    public static void close(AutoCloseable closeable) {
         org.dromara.hutool.core.io.IoUtil.closeQuietly(closeable);
    }
}

